//---------------------------------------------------------------------------
// MyConMan Class
// Author : Alex Woo-Hyun Kim at www.webrobot.org
// Email : woorung@webrobot.org,webrobot@hotmail.com,woorung@empal.com
// Date : Apr. 17, 2002
//
// This class manage connection pool to MySQL by means of circular queue.
//---------------------------------------------------------------------------
#include <stdio.h>
#include <iostream>
#include <string>
#include <stdlib.h>
#include <mysql.h>

#include "CMyConMan.h"
#include "CStringTokenizer.h"
//---------------------------------------------------------------------------
// initialize global variable related to reference count
unsigned int CMyConMan::g_iRefCount = 0;

// initialize global variables related to connection pool
unsigned int CMyConMan::g_iMaxCon = 0;
unsigned int CMyConMan::g_iCurPos = 0 ;
TMyConPool *CMyConMan::g_pMyConPool = NULL ; // it will be circular queue
//---------------------------------------------------------------------------
CMyConMan::CMyConMan(char *pszConfigFileName)
{
#ifdef DEBUG
	Debug("Current Reference Count is [");
        Debug(g_iRefCount);
        Debug("]\n");
#endif
	if(g_iRefCount==0) {
#ifdef DEBUG
		Debugln("This is first object, so g_pMyConPool will be allocated memory");
#endif
		// initialize member variables related to configuration file for mysql
        	m_pszHost = NULL ;
        	m_pszPort = NULL ;
        	m_pszSocket = NULL;
        	m_pszUser = NULL;
        	m_pszPasswd = NULL;
        	m_pszDB = NULL;
        	m_pszFlags = NULL ;

		if(g_pMyConPool==NULL) GetConfigFile(pszConfigFileName);

		// get ,in advance, real connection to MySql to create connection pool
		if(GetRealConnections()==false) {
			// error!!
			printf("\nError : all connection exhausted\n");
		}
	}

	g_iRefCount++;
}
//---------------------------------------------------------------------------
CMyConMan::~CMyConMan()
{
	g_iRefCount--;

#ifdef DEBUG
	Debug("\nCurrent Reference Count is [");
	Debug(g_iRefCount);
	Debug("]\n");
#endif 

	FreeRealConnections();

	if(m_pszHost!=NULL) delete []m_pszHost;
	if(m_pszPort!=NULL) delete []m_pszPort;
	if(m_pszSocket!=NULL) delete []m_pszSocket;
	if(m_pszUser!=NULL) delete []m_pszUser;
	if(m_pszPasswd!=NULL) delete []m_pszPasswd;
	if(m_pszDB!=NULL) delete []m_pszDB;
	if(m_pszFlags!=NULL) delete []m_pszFlags;
}
//---------------------------------------------------------------------------
MYSQL *CMyConMan::GetConnection()
{
	int i; // only a circle
	int count;

	for(i=0;i<g_iMaxCon;i++) {
		// use modulo to implement circular queue	
		if(g_pMyConPool[g_iCurPos%g_iMaxCon].bUse==false) {
			count = g_iCurPos%g_iMaxCon;

#ifdef DEBUG
			Debug("\nCurrent GetConnection is [");
			Debug(count);
			Debug("]\n");
#endif
			g_iCurPos++;
			if(g_iCurPos>=g_iMaxCon) g_iCurPos = 0;
			g_pMyConPool[count].bUse = true;
			return g_pMyConPool[count].pMyCon;
		} 

		g_iCurPos++;
                if(g_iCurPos>=g_iMaxCon) g_iCurPos = 0;
	}

	// when all of connections are using, you must require more detail processing
	if(i==g_iMaxCon) return NULL; 
}
//---------------------------------------------------------------------------
void CMyConMan::FreeConnection(MYSQL *pMyCon)
	
{
	int i; // only a circle

	for(i=0;i<g_iMaxCon;i++) {
		if(g_pMyConPool[g_iCurPos%g_iMaxCon].pMyCon==pMyCon) {
			g_pMyConPool[g_iCurPos%g_iMaxCon].bUse = false;	

#ifdef DEBUG
			Debug("\nCurrent FreeConnection is [");
			Debug(g_iCurPos);
			Debug("]\n");
#endif
			return;
		}

		g_iCurPos++;
		if(g_iCurPos>=g_iMaxCon) g_iCurPos = 0;
	}		
	
	return;
}
//---------------------------------------------------------------------------
bool CMyConMan::GetRealConnections()
{
	// Aaccept only one connection to MySql.
	// If you wanna get some connection for pool, you can delete the following. 
	// Because we have already obtained the value of g_iMaxCon from configuration file.
	//g_iMaxCon = 1;

	g_pMyConPool = new TMyConPool[g_iMaxCon];

	for(int i=0;i<g_iMaxCon;i++) {
		g_pMyConPool[i].bUse = false;
		g_pMyConPool[i].pMyCon = mysql_init(NULL);
		if(g_pMyConPool[i].pMyCon==NULL) {
#ifdef DEBUG
			Debugln("mysql_init fails!!");
#endif
			return false;	
		}			

		if(mysql_real_connect(g_pMyConPool[i].pMyCon,m_pszHost,m_pszUser,m_pszPasswd,m_pszDB,atoi(m_pszPort),m_pszSocket,0)==NULL) {
//		if(mysql_real_connect(g_pMyConPool[i].pMyCon,"localhost","","","dic",0,NULL,0)==NULL) {
#ifdef DEBUG
			Debugln("mysql_real_connect fails!!");
			Debug(m_pszHost);
			Debug(m_pszUser);
			Debug(m_pszPasswd);
			Debugln(m_pszDB);
#endif
			return false;
		}
	}

	return true;
}
//---------------------------------------------------------------------------
void CMyConMan::FreeRealConnections()
{
	if(g_iRefCount==0) {
                if(g_pMyConPool!=NULL) {
			for(int i=0;i<g_iMaxCon;i++) {
				//g_pMyConPool[i].bUse = false;
				mysql_close(g_pMyConPool[i].pMyCon);
			}
			delete []g_pMyConPool;
                	g_pMyConPool = NULL;
		}

#ifdef DEBUG
                Debugln("This is final object, so all of g_pMyConPool will be released");
#endif
        }
}
//---------------------------------------------------------------------------
bool CMyConMan::GetConfigFile(char *pszConfigFileName)
{
	FILE *hConfigFile;
	int iFileSize;
	char *pszStr;
	CStringTokenizer *pToken;
	char *pszName;
	char *pszValue;
	char *pszConfigVariables[8] = { 
                "MaxCon",       // Maximum Connection Count
                "Host",         // MySql Host Name
                "Port",         // MySql Service Port   
                "Socket",       // Mysql Service Socket 
                "User",         // User Name
                "Passwd",       // User Password 
                "DB",           // Database Name
                "Flags"         // Extra Flags                          
        };

	hConfigFile = fopen(pszConfigFileName,"r");
	if(hConfigFile==NULL) return false;
	fseek(hConfigFile,0,SEEK_END);
	iFileSize = ftell(hConfigFile);
	fseek(hConfigFile,0,SEEK_SET);
	pszStr = new char[iFileSize+1];	

	if(fread(pszStr,sizeof(char),iFileSize,hConfigFile)<=0) {
		fclose(hConfigFile);
		return false;
	}
	fclose(hConfigFile);

#ifdef DEBUG
	Debug("\n[configuration file content]\n");
	Debug(pszStr);
	Debug("\n");	
#endif

	pToken = new CStringTokenizer(pszStr,"\r\n","#");
#ifdef DEBUG
	Debug("[names and values]\n");
#endif
	while(pToken->HasMoreElements()) {
#ifdef DEBUG
		Debug("original sentence is [");
		Debug(pToken->GetNextElement());
		Debug("]\n");
		pToken->UndoGetNextElement();
#endif
		CStringTokenizer *pSubToken = new CStringTokenizer(pToken->GetNextElement()," =\t");
		pszName = pSubToken->GetNextElement();
		if(pSubToken->HasMoreElements()) pszValue = pSubToken->GetNextElement();
		else pszValue = "";

		SetConfigVariable(pszName,pszValue,pszConfigVariables);
		delete pSubToken;
	}
#ifdef DEBUG
	Debugln("\n[configuration variables]");
	Debugln(m_pszHost);
	Debugln(m_pszUser);
	Debugln(m_pszPasswd);
	Debugln(m_pszDB);
#endif

	delete pToken;
	delete []pszStr;

	return true;
}
//---------------------------------------------------------------------------
void CMyConMan::SetConfigVariable(char *pszName,char *pszValue,char **pszConfigVariables)
{
	char *pszVariable;
	int iVariableSize;
	int iFound;

#ifdef DEBUG
	Debug("\"");
	Debug(pszName);
	Debug("\"");
	Debug("\t\t");
	Debug("\"");
	Debug(pszValue);
	Debug("\"");
	Debug("\t\t");
#endif

	for(iFound=0;iFound<8;iFound++) {
		pszVariable = pszConfigVariables[iFound];
		iVariableSize = strlen(pszVariable);
		if(!strncmp(pszVariable,pszName,iVariableSize)) break;
	}
	if(iFound==8) return; // in case of being not found
	else {
#ifdef DEBUG
		Debug(pszConfigVariables[iFound]);
		Debug("\n");
#endif
		switch(iFound) {
			case 0 :
				g_iMaxCon = atoi(pszValue);
				break;
			case 1 :
				m_pszHost = new char[iVariableSize+1];
				strcpy(m_pszHost,pszValue);
				break;
			case 2 :
				m_pszPort = new char[iVariableSize+1];
				strcpy(m_pszPort,pszValue);
				break;
			case 3 :
				m_pszSocket = new char[iVariableSize+1];
				strcpy(m_pszSocket,pszValue);
				break;
			case 4 :
				m_pszUser = new char[iVariableSize+1];
				strcpy(m_pszUser,pszValue);
				break;
			case 5 :
				m_pszPasswd = new char[iVariableSize+1];
				strcpy(m_pszPasswd,pszValue);
				break;
			case 6 :
				m_pszDB = new char[iVariableSize+1];
				strcpy(m_pszDB,pszValue);
				break;
			case 7 :
				m_pszFlags = new char[iVariableSize+1];
				strcpy(m_pszFlags,pszValue);
				break;
		}
	}
}
//---------------------------------------------------------------------------
#ifdef DEBUG
void CMyConMan::Debug(char *str)
{
	FILE *f = fopen("debug","a+");
	fprintf(f,"%s",str);
	fclose(f);
}
void CMyConMan::Debugln(char *str)
{
	FILE *f = fopen("debug","a+");
	fprintf(f,"%s\n",str);
	fclose(f);
}
void CMyConMan::Debug(int i)
{
	FILE *f = fopen("debug","a+");
	fprintf(f,"%d",i);
	fclose(f);
}
void CMyConMan::Debugln(int i)
{
	FILE *f = fopen("debug","a+");
	fprintf(f,"%d\n",i);
	fclose(f);
}
#endif
//--------------------------------------------------------------------------
